// Copyright 2011, Burt Harris
// All rights reserved

namespace YamlEditor
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.Composition;
    using System.Linq;
    using Microsoft.VisualStudio.Text;
    using Microsoft.VisualStudio.Text.Classification;
    using Microsoft.VisualStudio.Text.Tagging;
    using Microsoft.VisualStudio.Utilities;
    using YaTools.Yaml;

    public struct YamlTokenTag : ITag
    {
        internal YaTools.Yaml.Token Token { get; private set; }

        internal YamlTokenTag(YaTools.Yaml.Token token) : this()
        {
            this.Token = token;
        }
    }
       
    public class YamlTagger : ITagger<ClassificationTag>, ITagger<YamlTokenTag>
    {
        IClassificationTypeRegistryService _classificationTypeRegistry;
        SeekableYamlScanner _scanner = new SeekableYamlScanner();
        Dictionary<Token, IClassificationType> _classification= new Dictionary<Token, IClassificationType>();

        internal YamlTagger(IClassificationTypeRegistryService registry)
        {
            this._classificationTypeRegistry = registry;
            _classification[Token.Directive] = registry.GetClassificationType("yaml.directive");
            _classification[Token.Comment] = registry.GetClassificationType("yaml.comment");
        }

        IEnumerable<ITagSpan<YamlTokenTag>> ITagger<YamlTokenTag>.GetTags(NormalizedSnapshotSpanCollection spans)
        {
            // For now, we're going to tokenize the whole buffer, optimize later

            Token token;
            var snapshot = spans[0].Snapshot;
            _scanner.SetSource(snapshot.GetText());

            while ((token = _scanner.NextToken()) != Token.EndOfStream)
            {
                var span = new Span( _scanner.TokenStart.Index, _scanner.TokenEnd.Index);
                var snapshotSpan = new SnapshotSpan(snapshot, span);
                if (spans.Any(x=>x.IntersectsWith(snapshotSpan)))
                {
                    yield return new TagSpan<YamlTokenTag>( snapshotSpan, new YamlTokenTag(_scanner.Token));
                }
            }
        }

        event EventHandler<SnapshotSpanEventArgs> ITagger<YamlTokenTag>.TagsChanged
        {
            add { throw new NotImplementedException(); }
            remove { throw new NotImplementedException(); }
        }


        public event EventHandler<SnapshotSpanEventArgs> TagsChanged;

        public IEnumerable<ITagSpan<ClassificationTag>> GetTags(NormalizedSnapshotSpanCollection spans)
        {
            foreach (var yamlTagSpan in ((ITagger<YamlTokenTag>)this).GetTags(spans))
            {
                IClassificationType type;
                if (_classification.TryGetValue(yamlTagSpan.Tag.Token, out type))
                    yield return new TagSpan<ClassificationTag>(yamlTagSpan.Span, new ClassificationTag(type));
            }
        }

        // Factory class

        [Export(typeof(ITaggerProvider))]
        [ContentType("yaml")]
        [TagType(typeof(YamlTokenTag))]
        [TagType(typeof(ClassificationTag))]
        internal class TaggerProvider : ITaggerProvider
        {
            [Import]
            internal IClassificationTypeRegistryService ClassificationRegistry = null;
            
            public ITagger<T> CreateTagger<T>(ITextBuffer buffer) where T : ITag
            {
                return (ITagger<T>)new YamlTagger(ClassificationRegistry);
            }
        }

    }
}
